using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Avivo
{
	public static class Program
	{
		public static void Main(string[] args)
		{
			//Show help if required arguments are missing
			if (args == null || args.Length < 1 || Regex.IsMatch(args[0], @"^(\-|\-\-|\/)help$", RegexOptions.IgnoreCase))
			{
#if JPEG
				Console.WriteLine("Usage:");
				Console.WriteLine("  html2jpg <options> [input.html] [output.jpg]");
				Console.WriteLine("");
				Console.WriteLine("Options:");
				Console.WriteLine("  --width=<int>   Capture area and image width in pixels");
				Console.WriteLine("  --height=<int>  Capture area and image height in pixels");
				Console.WriteLine("");
				Console.WriteLine("Examples:");
				Console.WriteLine("  html2jpg test.html");
				Console.WriteLine("  html2jpg test.html test.jpg");
				Console.WriteLine("  html2jpg http://www.example.com");
				Console.WriteLine("  html2jpg --width=1920 --height=1080 test.html test.jpg");
#else
				Console.WriteLine("Usage:");
				Console.WriteLine("  html2png <options> [input.html] [output.png]");
				Console.WriteLine("");
				Console.WriteLine("Options:");
				Console.WriteLine("  --width=<int>   Capture area and image width in pixels");
				Console.WriteLine("  --height=<int>  Capture area and image height in pixels");
				Console.WriteLine("");
				Console.WriteLine("Examples:");
				Console.WriteLine("  html2png test.html");
				Console.WriteLine("  html2png test.html test.png");
				Console.WriteLine("  html2png http://www.example.com");
				Console.WriteLine("  html2png --width=1920 --height=1080 test.html test.png");
#endif
				return;
			}

			//Attach global exception handler
			AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

			int? width = null;
			int? height = null;

			//Parse command line arguments
			foreach (string arg in args)
			{
				Match match = Regex.Match(arg, @"^(\-|\-\-)(?<name>[\w\d]+)[:=](?<value>\d+)");
				if (match.Success)
				{
					string option = match.Groups["name"].Value;
					string value = match.Groups["value"].Value;
					
					switch (option)
					{
						case "w":
						case "width":
							width = int.Parse(value);
							break;

						case "h":
						case "height":
							height = int.Parse(value);
							break;

						default:
							throw new Exception("Unknown option: " + arg);
					}
				}
			}
			
			//Source file and output file
			string source = null;
			string imagePath = null;

			if (args.Length >= 2 && !Regex.IsMatch(args[args.Length - 2], @"^(\-|\-\-)", RegexOptions.IgnoreCase))
			{
				//Last two arguments are file names
				source = args[args.Length - 2];
				imagePath = args[args.Length - 1];
			}
			else
			{
				//Only last argument is file name
				source = args[args.Length - 1];

				string name = Path.GetFileName(source);
				if (string.IsNullOrEmpty(name))
				{
					//Random name
					name = Guid.NewGuid().ToString("N");
				}
#if JPEG
				imagePath = name + ".jpg";
#else
				imagePath = name + ".png";
#endif
			}

			//Check the files
			Uri uri = default(Uri);
			if (!Uri.TryCreate(source, UriKind.Absolute, out uri))
			{
				FileInfo file = new FileInfo(source);

				//Check if file exists
				if (!file.Exists)
				{
					throw new FileNotFoundException("Input file does not exist! File: " + file.Name);
				}

				//Ensure that absolute path is used
				source = file.FullName;
			}
			
			//Ensure the output directory exists
			FileInfo imageFile = new FileInfo(imagePath);
			if (!imageFile.Directory.Exists)
			{
				imageFile.Directory.Create();
			}

			//Finally generate the image
			Generate(width, height, source, imagePath);
		}

		public static void Generate(int? width, int? height, string source, string imagePath)
		{
			//Initialize HTML to image converter
			WebsiteToImage converter = new WebsiteToImage();
			converter.Width = width;
			converter.Height = height;

			//Load HTML and capture bitmap image
			using (Bitmap bitmap = converter.Generate(source))
			{
#if JPEG
				bitmap.Save(imagePath, ImageFormat.Jpeg);
#else
				bitmap.Save(imagePath, ImageFormat.Png);
#endif
			}
		}

		public static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
		{
			//Report error and exit with exit code 1
			Exception error = e.ExceptionObject as Exception;
			Console.WriteLine(error.Message);
			Environment.Exit(1);
		}
	}

	/// <summary>
	/// Converts HTML to image
	/// </summary>
	/// <remarks>
	/// Reference: http://stackoverflow.com/questions/2715385/convert-webpage-to-image-from-asp-net
	/// </remarks>
	public class WebsiteToImage
	{
		private Bitmap bitmap;
		private string url;

		public int? Width;
		public int? Height;

		public Bitmap Generate(string url)
		{
			this.url = url;
			Thread thread = new Thread(generate);
			thread.SetApartmentState(ApartmentState.STA);
			thread.Start();
			thread.Join();
			return bitmap;
		}

		private void generate()
		{
			WebBrowser browser = new WebBrowser();
			browser.ScrollBarsEnabled = false;
			browser.ScriptErrorsSuppressed = true;
			browser.Navigate(url);
			browser.DocumentCompleted += WebBrowser_DocumentCompleted;
			while (browser.ReadyState != WebBrowserReadyState.Complete)
			{
				Application.DoEvents();
			}
			browser.Dispose();
		}

		private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
		{
			//Capture
			WebBrowser browser = sender as WebBrowser;

			int w = (this.Width != null ? this.Width.Value : browser.Document.Body.ScrollRectangle.Width);
			int h = (this.Height != null ? this.Height.Value : browser.Document.Body.ScrollRectangle.Bottom);

			browser.ClientSize = new Size(w, h);
			browser.ScrollBarsEnabled = false;

			bitmap = new Bitmap(w, h);
			browser.BringToFront();
			browser.DrawToBitmap(bitmap, browser.Bounds);
		}
	}
}
